home *** CD-ROM | disk | FTP | other *** search
- /*
- HASUtilCursors.c from Hsoi's App Shell. © 1995-1997 John C. Daub. All rights reserved.
-
- This file contains various cursor related functions and utilities.
- */
-
- #pragma mark ••• #includes •••
-
- #ifndef _WASTE_
- #include "WASTE.h"
- #endif
- #include "HASGlobals.h"
- #ifndef __HSOIS_APP_SHELL__
- #include "HASMain.h"
- #endif
- #include "HASUtilCursors.h"
- #include "HASUtilDialogs.h"
- #include "HASUtilities.h"
- #include "HASSoundSpeech.h"
- #ifndef __QUICKDRAW__
- #include <QuickDraw.h>
- #endif
- #ifndef __RETRACE__
- #include <Retrace.h>
- #endif
- #ifndef __CURSORCTL__
- #include <CursorCtl.h>
- #endif
-
-
- #pragma mark -
- #pragma mark ••• Init Cursors •••
-
- // HsoiSetUpCursors() is called at startup time to get our various cursors set up.
- // i like obtaining Handles to frequently used cursors now and using these globals
- // in SetCursor() instead of calling GetCursor() all the time (avoid accessing the
- // toolbox/rom's to save a little time).
-
- void HsoiSetUpCursors( void )
- {
- // get our i-beam and static (not animated) watch cursors
-
- gEditCursor = GetCursor(iBeamCursor);
- gWaitCursor = GetCursor(watchCursor);
-
- // move and lock them high in the application heap
-
- HLockHi( (Handle)gEditCursor );
- HLockHi( (Handle)gWaitCursor );
-
- return;
- }
-
-
- #pragma mark -
- #pragma mark ••• Manual Spin Cursor •••
-
- OSErr HsoiStartManualSpinning( void )
- {
- OSErr err = noErr;
-
- // now get our animated wait cursor
-
- // get a handle to the 'acur' resource..
-
- gAnimWatchCursor = (acurHandle)GetResource( TYPE_ANIMATED_CURSOR, rSpinningWatchACUR );
-
- if ( gAnimWatchCursor == nil ) // didn't get it, probably not there or not enough memory to load it
- HsoiDoError( rErrorStrings, errFailGetResource, ResError(), kErrDeath ); // death, i know..extreme
-
- // move it high in the heap so we don't fragment our memory
-
- MoveHHi( (Handle)gAnimWatchCursor );
- err = ResError();
- if ( err != noErr )
- {
- // if the handle is locked (i.e. we can't move it)
-
- if ( err == memLockedErr )
- {
- HUnlock( (Handle)gAnimWatchCursor );
- MoveHHi( (Handle)gAnimWatchCursor );
- HLock( (Handle)gAnimWatchCursor );
- err = noErr;
- }
- else
- {
- HsoiDoError( rErrorStrings, errFailGetResource, err, kErrDeath );
- }
- }
-
- // initialize the rotation routines (glue for these, in CW, is in the
- // ToolLibs.o). (right now, this isn't a VBL task...i.e. if you want the
- // cursor you spin, you'll have to do it all yourself. see my sample
- // movable modal dialog (the progress bar thing) for an example of this)
-
- // or, see later on in this file for how to do a VBL spinning cursor (i.e.
- // you don't have to do it all yourself!)
-
- InitCursorCtl( gAnimWatchCursor );
-
- SpinCursor( 0 ); // get our cursor ready to spin!
-
- return err;
- }
-
- OSErr HsoiStopManualSpinning( void )
- {
- OSErr err = noErr;
-
- // unlock the animated cursor resources
-
- HUnlock( (Handle)gAnimWatchCursor );
-
- // make sure it can be purged
-
- HPurge( (Handle)gAnimWatchCursor );
-
- // and dispose of it
-
- HsoiForgetResource( (Handle *)&gAnimWatchCursor );
-
- // set the cursor to the arrow
-
- InitCursor();
-
-
- return err;
- }
-
- #pragma mark -
- #pragma mark ••• Other Cursor Utils •••
-
- // HsoiIBeamIt() would be called in a dialog filter so that it's called "constantly" to
- // track the movement of the cursor. If the cursor moves over an active edit line, the
- // cursor is changed to an I-Beam cursor, else an arrow.
-
- // this function is not necessary for HAS since we're under System 7 (at least), cause
- // system 7's dialog manager provides a routine (SetDialogTracksCursor()) to do this for us.
- // but, i'll leave the code in here just in case you want to know about this sort of thing
- // (cause tracking a cursor is something we all need to know how to do)
-
- void HsoiIBeamIt(WindowRef window)
- {
- Point mouseAt;
- Rect borderRect;
- short itemNum;
-
- // first get the current edit line out of the dialog record
-
- itemNum = GetDialogKeyboardFocusItem(window);
- HsoiGetDialogItemRect( window, itemNum, &borderRect );
-
- // find where the mouse is at
-
- GetMouse(&mouseAt);
-
- // if it's within our dialog item, change to the i-beam else the arrow
-
- if (PtInRect(mouseAt, &borderRect))
- {
- SetCursor( *gEditCursor );
-
- // or, you could call it like this:
- // SetCursor(*(GetCursor(1)));
- // if you don't want dependance on the HAS globals
-
- }
- else
- {
- // This means I'm callng InitCursor every time through my filter
- // this is a little excessive (ha!) but doesn't hurt anything.
- // You could set a global or something if this really offends you
-
- InitCursor();
-
- }
-
- return;
- }
-
-
- // HsoiAdjustCursor() is called in the main event loop. it finds where the
- // cursor is and then adjusts it's appearance based upon the mouse's location
- // on the screen
-
- void HsoiAdjustCursor( Point mouseLoc, RgnHandle mouseRgn )
- {
- WindowRef window;
-
- /* by default, set mouseRgn to the whole QuickDraw corrdinate plane, */
- /* so that we never get mouse moved events */
-
- SetRectRgn( mouseRgn, -MAXSHORT, -MAXSHORT, MAXSHORT, MAXSHORT );
-
- /*
- the cursor is set to the watch cursor by DoMovableModalDialog(), and removed (set back
- to the arrow or whatever) when DialogHit() is called or the movable modal dialog
- is dismissed. Futhermore, it is necessary to make sure that WEAdjustCursor() does
- check if gPeriodicTask, cause otherwise it'll change it!
- */
- if ( gPeriodicTask )
- return;
-
- /* give text services a change to set the cursor shape */
-
- if ( gHasTextServices )
- {
- if ( SetTSMCursor( mouseLoc ) )
- return;
- }
-
- /*
- if there is a window open, give WEAdjustCursor() an opportunity to set the cursor.
- WEAdjustCursor() insersects mouseRgn (if supplied) with a region within which
- the cursor is to regain its shape. if the cursor is outside the view region,
- this is subtracted from mouseRgn.
-
- and note, only call WEAdjustCursor if there is a window, the window is a
- document window (at least needs a WASTE instance attached to it, but we
- don't want the clipboard window to get an edit cursor else that might "tell" the
- user they can edit the clipboard window), and there isn't a periodic task in
- effect (but this is already taken care of above).
- */
-
- window = FrontWindow();
-
- if ( (window != nil) && HsoiIsDocumentWindow( window ) )
- {
- if ( gHiliting )
- {
- InitCursor();
- return;
- }
-
- if ( WEAdjustCursor( mouseLoc, mouseRgn, HsoiGetWindowWE(window) ) )
- return;
- }
-
-
- /* if we made it this far, set the cursor to the arrow cursor */
-
- // don't use InitCursor() cause that'll make the cursor flicker cause InitCursor()
- // shows the cursor (i think it calls ShowCursor()). using SetCursor will set
- // it properly to an arrow, but if it's hidden, it will remain hidden.
-
- SetCursor( &qd.arrow );
-
- return;
- }
-
-
- #pragma mark -
- #pragma mark ••• VBL Spinning Cursor •••
-
- /*
- what follows here is code related to spinning a cursor. but this isn't any old
- spin cursor stuff...it's spinning a cursor as a VBL Task! Just what is a VBL
- Task? well first, VBL stands for Vertical Blanking Interrupt. This is something
- generated by the Vertical Retrace Manager, "the part of the Operating System
- that schedules and executes recurrent tasks during vertical retrace interrupts.
- You can use the Vertical Retrace Manager to execute simple, repetitve tasks and
- avoid having to execute those tasks repeatedly in your application's main event
- loop" (Inside Macintosh: Processes, 4-3).
-
- Here's the jist of how it works. (this is taken from Inside Macintosh: Processes,
- 4-4).
-
- The video circuitry in a Macintosh computer, whether built-in or external, refreshes
- the screen at regular intervals. For built-in monitors, the screen is refreshed
- approximately 60 times per second; for external monitors, the screen is refreshed at
- intervals determined by the associated video hardware. To refresh the screen, the
- monitor's electron beam draws one pixel at a time, starting at the upper-left corner
- of the screen and moving quickly to the lower-right corner. When the electron beam
- returns from the lower-right corner of the screen to the upper-left corner, the video
- circuitry generates a "vertical retrace interrupt" or "vertical blanking (VBL) interrupt".
-
- The Vertical Retrace Manager is the part of the Operating System that schedules and
- executes tasks -- known as VBL tasks -- during a vertical retrace interrupt. The
- Operating System itself uses the Vertical Retrace Manager to perform some important
- housekeeping operations, such as moving the cursor in response to mouse
- movements and checking whether the current application's stack has expanded into it's
- heap. Within the limitations described in this chapter [Chapter 4 of IM:Processes],
- you can use the VRMgr to install your own recurrent tasks [like the spinning cursor
- we're about to do].
-
- In general, the VRMgr is useful for small, repetitive tasks that do not allocate or
- release memory and that you do not want to execute in your main event loop. Whenever
- possible, it is best to manage periodic tasks within your main event loop [for
- example, the sample movable modal dialog coupled with HsoiDoMainLoopTasks]. However,
- if you want some task some task to execute repetitvely at a time when you do not want
- to reenter your main event loop (perhaps because you don't want your application to
- be switched out during some lengthy operation), it might be possible to use the VRMgr
- to execute the task.
-
- The principal limitation on VBL tasks (aside from the limitations on any interrupt-time
- processing) is that they cannot execute more frequently than once per VBL interrupt.
- [the exact timing of a VBL interrupt will vary from monitor to monitor based upon
- that monitor's refresh frequency. If you need more exact timing and/or more often
- execution than a VBL task would allow, use the Time Manager, explained in IM: Processes.
- Additionally, the VRMgr is NOT an absolute timing mechanism...it's always relative
- to VBL interrupts. If absolute time delays are important, use the Time Manager.
- VRMgr stuff is good in cases where the scheduled actions need simply to be synchronized
- with other VBL tasks, such as moving the cursor or refreshing the screen].
-
- I don't want to include all of Chapter 4 of IM:Processes here (you can get this
- at your local bookstore, or even off the Internet from Apple's ftp and web
- sites...try http://dev.info.apple.com/). But this should be sufficient to explain
- just what the heck a VBL task is and how it works so you can understand what i'm
- about to do. And just as an fyi sorta thing, here are the VBL tasks installed
- by the MacOS (from IM: Processes, 4-5):
-
- Every interrupt:
- • update the value of the global variable "Ticks" which a program may access
- throughout the routine TickCount()
- • call the "stack sniffer" to see if the current application's stack and heap
- have collided. If so, the task calls the System Error Handler
- • update the position of the cursor
-
- Every 30 interrupts
- • Check whether the user has inserted a disk or a mounted a volume. If so,
- the task posts a disk-inserted event.
-
- Evert 32 interrupts
- • Check whether a keyboard has been reattached after having been detached. If
- so, the task resets the keyboard.
-
- incidentally, some VBL routines may execute only on certain computers or only in
- certain versions of system software. Also, VBL tasks installed by the OS are not
- maintained in the same queue used for application-defined VBL tasks.
-
- So, that should be more than enough for you to start understanding VBL tasks and
- this spinning cursor stuff I'm about to do. If you'd like to read more about
- this sort of thing (and you should!), check out Inside Macintosh. I do highly
- recommend that you do read IM:Processes (all of it, it's pretty short) if you
- want to do anything with VBL tasks...it's a pretty easy thing to use, but there
- are lots of things to be careful about. it's too much stuff to list here, so
- getting your own copy of the book is best.
- */
-
- /*
- Now, some other notes :)
-
- There are all sorts of ways to spin the cursor. I impliment a nice way of doing
- this with my movable modal dialog progress bar thing (from the Dialogs menu,
- select MovableModal Dialog).
-
- I've also seen lots of sample code that can do VBL tasks. For example, in Scott
- Knaster's book "Macintosh Programming Secrets" 2nd ed. Chapter 10, Knaster gives
- us a wonderful VBL spinning cursor. But there is a problem with Knaster's code.
- It uses assembly language! Now, this really isn't a problem, in fact it's
- pretty cool. However, I would prefer to not use any assembly in HAS. why?
- well, how many people out there know assembly? (if you don't, you should at least
- get familiar with it for debugging and optimization purposes...but that's an
- advanced thing...and fyi, 2 good books for learning this sort of thing, tho
- a bit old and out of date, are the MacsBug Reference and Debugging Guide
- (from Apple Computer. as of this writing, it covers MacsBug 6.2 tho the latest
- version of Macsbug is 6.5.2, so a little out of date, but still relevant) and
- Scott Knaster's How to Write Macintosh Software (currently in it's 3rd edition
- and desperately, IMHO, in need up and update since it's all in Pascal and
- doesn't cover PowerMacs and assembly for PowerPC chips, only assembly
- for 680x0 chips)).
-
- Also, the code in Knaster's MPS book is made for THINK C (not CodeWarrior).
- so the changes would be problematic (actually, I'm just lazy).
-
- And also, this assembly probably won't work on PowerMacs! (dunno, haven't
- tried it, but I remember something clicked in my mind knowing that the code
- won't work...I think it's cause of the A5 register stuff and storing stuff
- at A0).
-
- So, what I've got here is basically taken right out of IM:Processes (with
- some appropriate and necessary modifications), and perhaps a bit of
- Knaster-knowledge thrown in.
-
- Incidentally, tho Knaster's code probably can't work in this sort of situation
- I'm going for, his explanations and stuff is still worth a million bucks.
- I still say that if you have the money, get Knaster's book(s).
-
- OH!...one last thing. right now I'm only implimenting this spinning cursor
- in CreateWindow() when/if there is a file to read in. To test all this stuff,
- i created a HUGE text file (i saved my Help stuff as a text file, then select
- all, copy, paste...then select all again, copy paste...etc...did it over and
- over...the file is about 842k (text, styl, soup, etc) all in all..takes some
- time to open up needless to say! Things seem to work ok.)
-
- Now, while i was creating this file, with all the pasting i was doing (and
- as i progressed, the pastes got bigger and bigger) it was taking some substantial
- time (at least substantial on my IIvx with it's 68030 @ 32mHz) to paste!
- so, if you'd like to in your program (or your toying with HAS) you might want
- to use this VBL code anytime you would need a wait cursor (pasting is definately
- one place, there could be others...you decide). One nice thing about this code
- is that the spinning cursor isn't activated immediately...a couple seconds go
- by before the spinning cursor happens. this way, if the operation you're performing
- takes a very short time (like less than a couple seconds) the "wait" cursor
- never kicks in (tho the VBL task was installed). then if the operation was
- taking a long time, the spinning wait cursor happens...joys of VBL eh? you'd
- not really be able to determine such a thing normally...like i could determine
- all this "manually" with something like my HsoiDoMainLoopTasks, but with
- something like pasting (calling WEPaste), you'd have to patch WEPaste, that's
- not recommended to do (else risk non-conformity with the WASTE API and you'd
- have to "reinstall" your custom hacks to WASTE every time a new WASTE API was
- released).
-
- so, this is a cool thing eh?
-
- but one thing to be wary about...if you had a spinning cursor then switched
- your app into the background, you could have problems...(haven't tried this
- myself yet, but i'm sure it'd be a problem). be careful about this...
- layer switching (i.e. suspend/resume events) with an application-specific-active
- VBL task installed could leave the VBL task running and make for a confused
- user. right now we get around this by calling Start and Stop around and
- in functions where we know a layer switch could never occur. In this sort
- of instance, i can think of 2 possible things to do: 1. create something
- similar to the HsoiDoMainLoopTasks and my sample movable modal dialog.
- that coupled with the rest of the event loop (and the rest of HAS's structuring)
- makes things work ok. 2. you could do something in your suspend/resume
- event handling to check for your application's VBL tasks and remove/reinstall
- them at suspend/resume time. (actually, as long as you call these VBL
- tasks around/in a function that doesn't bother with the main event loop (like
- how i'm using it solely in HsoiCreateWindow), you're ok...if you want to
- learn more about this sort of thing, IM:Processes covers all the details
- you'd need to know about VBL's and application execution with major/minor
- context switches, etc...again, another good reason to get IM:Processes)
-
- also, one thing I'm curious about....there are 2 general types of VBL tasks.
- a system-based VBL task (where the OS maintains just one task queue and processes
- the tasks in that queue when it receives a VBL interrupt and is not linked to
- any external device, like a monitor). This is what I am currently doing.
-
- however, one thing I've tried to do with HAS is demonstrate how to have code
- that's happy in multiple monitor setups (like HsoiDoDrag()) since so few people
- know how to do this and so few apps are happy with multiple monitors (something
- you should strive for: an app happy regardless (pretty much) of the user's
- computer setup). There is another VBL task type: a slot-based VBL task.
- this type of task is linked to an external video monitor. because different
- monitors can have different refresh rates and hence might execute VBL tasks
- at different times, the VRMgr maintains a separate task queue for each video
- device attached to the computer. then when a vbl interrupt occurs for a
- particular device, the VRMgr executes any tasks in the queue for the slot holding
- that monitor's video card. (and you'd then install a slot-based VBL task via
- SlotVInstall()).
-
- so, how might this code behave (and break?) in multiple monitor setups? if
- the cursor got moved onto another monitor while the task was executing, i'm
- certain the redraws (remember, cool thing with VBL tasks is that the animation
- would be synced with the screen refresh for nice animation) would fall
- differently (different monitors, different refresh rates) and cause funky
- drawing of the cursor...i hypothesize that the VBL task would still occur
- (just based upon the system VBL queue), but just (re)draw all funky, y'know?
-
- but then, it's hard for me to test and figure this out since i only have
- a single monitor at home :(
-
- I'd think what you'd want to do would be in the Start function, instead
- of just calling VInstall, you'd step through the device list and for
- each device call SlotVInstall...then i'd figure if they moved the cursor
- to another monitor, it'd work ok...and in the Stop function, do the
- same thing but call SlotVRemove. but again, this is all just my guess...
-
- and finally (i'm almost done, i hope), i hope that all my VBL code and
- usage falls within the parameters of using a VBL task (these rules are
- set forth in IM:Processes 4-6). here's hoping...but heck, this code
- is pretty much taken from Inside Macintosh..you'd think Apple wouldn't
- write bad code, y'know?
-
- So, I guess the bottom line of it all is, pray what i have works (sure using
- VInstall works, might flicker a bit on the redraws, but spinning a cursor
- doesn't require graphics precision like a game might), send me money to
- let me buy another monitor and a decent computer to use 2 monitors on! (just
- kidding!), and YOU get a copy of IM:Processes and read up!
-
- Last thing: i forgot just who helped me with this code! the code as written
- in IM:Processes did involve some assembly and wouldn't work on PowerMacs.
- I posted on comp.sys.mac.programmer.help and 2 guys replied to me (the joys
- of usenet news!). but i forgot the guys's names and email addresses! argh!
-
- well, one guy i know is named Skip...and i know i have their names around
- here somewhere....when i find them, i'll be sure to include them! the
- biggest thing they helped me with was modifying the code to work with PowerMacs
- and 68k Macs all from the same source. one of them had the right idea
- (the #pragma parameter) stuff, but it wasn't quite right, so i just had to do
- a little research and modification and viola! thanx guys!
-
- Now, enough talk, let's code!
- */
-
-
- // interesting to note, we're not going to use our 'acur' resource. we probably could,
- // but it's not worth the hassle
-
- // a little global for easy data access
-
- CursorTask gMyCursTask;
-
- // and a global UPP for our callback
-
- VBLUPP gMyVBLUPP = nil;
-
-
- // HsoiChangeCursor is the nice little function the VRMgr actually calls to "animate"
- // the cursor. This is what's installed into the VBLTask
-
- /*
- Ok...fun note...
-
- I originally did this a bit differently, using conditonal macros to get
- different function signatures and prototypes. Now in some places, this might
- work ok, but here, it doesn't work. Due to the nature of the VRMgr, 68k/PPC
- chips, and the MacOS, we have to actually perform things a bit strangely to
- get the same code to work on either chip.
-
- it worked fine on the 68k side of things...but when i tried it on a PowerMac,
- it died everywhere.... I got a bomb saying "Math Coprocessor not installed"
- and had to restart...
-
- now, this shouldn't even be an issue on the PowerMacs anyways, but oh well.
- there's a tech note somewhere explaining this strange error, but i won't sweat
- it now...
-
- point is, i took Skip's idea of using the #pragma parameter method. his
- code wasn't correct (it was off the top of his head), but that coupled with
- some info i got from the CW8 C/C++/Assembly Language Ref (QuickView database,
- just did a search through all the db's using "pragma" as the keyword) and found
- the info i needed. That gave me the proper syntax for this all.
-
- I have just tested it on a 68k and this method worked great! and i tried it
- on a PowerMac and it worked fine too. So, a nice way to get VBL's working regardless
- of the chip!
- */
-
- // the little magic that makes it all work.... :)
-
- #if !GENERATINGCFM
- #pragma parameter HsoiChangeCursor( __A0 )
- #endif
-
- // basically, if making a 68k app....
-
- #if !GENERATINGCFM
- void HsoiChangeCursor( CursorTaskPtr recPtr ); // function prototype
-
- void HsoiChangeCursor( CursorTaskPtr recPtr:__A0 ) // beginning of the actual function
- #else // PPC or CFM app
- void HsoiChangeCursor( CursorTaskPtr recPtr ); // function prototype
- void HsoiChangeCursor( CursorTaskPtr recPtr ) // beginning of actual function
- #endif
-
- // note how the conditional preprocessor macros work above to give us the right
- // prototype and function name/arguments for this following stuff....
-
- {
- // if the cursor is busy, we should not change it
-
- if ( !LMGetCrsrBusy() )
- {
- // update the cursor information
-
- // display the next cursor
-
- SetCursor( *(recPtr->myCursors[ recPtr->myFrame ]) );
-
- // advance to the next cursor frame
-
- recPtr->myFrame += 1;
-
- // and wrap around to the first frame
-
- if ( recPtr->myFrame > (kNumberOfCursors - 1) )
- recPtr->myFrame = 0;
- }
-
- // and set the task to run again (at every interrupt, the VRMgr decrements this
- // variable (vblCount) by one. when vblCount hits zero, the task is executed once.
- // to have the task executed again, we have to reset this variable back above zero
- // for the VRMgr to decrement. incidentally, if you didn't want to VRMgr to execute
- // this task again, you could just set/leave vblCount at zero. this is one way of
- // disabling a VBL task, so say you had a VBL task that could run during a major/minor
- // context switch (like suspend/resume), you could just set vblCount to 0 upon suspend
- // and it won't execute, then reset it to kInterval upon resume)
-
- recPtr->myVBLTask.vblCount = kInterval;
-
- return;
- }
-
-
-
-
- // HsoiStartVBLSpinning is our "public" routine to start the whole process. you call
- // this function from "wherever" (remember, there are limits/restrictions as to where you can
- // call this from) in your code that you want to start a spinning VBL cursor
-
- void HsoiStartVBLSpinning( void )
- {
- OSErr err;
- short count;
-
- // get our UPP set up
- if ( gMyVBLUPP == nil )
- gMyVBLUPP = NewVBLProc(HsoiChangeCursor);
-
- // initialize the cursor information
-
- for ( count = 0; count < kNumberOfCursors; count++ )
- {
- // load the cursor into memory
-
- gMyCursTask.myCursors[count] = GetCursor( kInitialResID + count );
-
- // lock the cursor so that we can call SetCursor at interrupt time
-
- HLockHi( (Handle)gMyCursTask.myCursors[count] );
- }
-
- // display cursor with kInitialResID first
-
- gMyCursTask.myFrame = 0;
-
- // initialize the VBL task record
-
- gMyCursTask.myVBLTask.qType = vType; // set the queue type
- gMyCursTask.myVBLTask.vblAddr = gMyVBLUPP; // get address of VBL task
- gMyCursTask.myVBLTask.vblCount = kInitialDelay; // set task frequency
- // notice by setting the initial delay much larger than the number of interrupts
- // between subsequent cursor changes, we prevent the cursor from starting to spin
- // until a reasonable time (about 2 seconds) has elapsed. adds a nice, cool
- // cosmetic touch!
-
- gMyCursTask.myVBLTask.vblPhase = 0; // no phase
-
- // make sure our cursor isn't hidden
-
- ShowCursor();
-
- // install it
-
- err = VInstall( (QElemPtr) &(gMyCursTask.myVBLTask) );
-
- return;
- }
-
- // and this goes along with HsoiStartVBLSpinning. i'm sure you can guess what it does, but if
- // not, HsoiStopVBLSpinning stops the VBL spinning cursor from spinning. It's probably safe
- // to say that any calls to HsoiStartVBLSpinning ought to be soon followed by a HsoiStopVBLSpinning
- // call (and there should be equal calls to these in your code, i.e. if you have 3 calls
- // to Start, there should be 3 calls to Stop...get why?)
-
- void HsoiStopVBLSpinning( void )
- {
- OSErr err;
- short count;
-
- // remove the task record from its queue
-
- err = VRemove( (QElemPtr) &(gMyCursTask.myVBLTask) );
-
- // free memory occupied by the cursors
-
- for ( count = 0; count < kNumberOfCursors; count++ )
- {
- HUnlock( (Handle)gMyCursTask.myCursors[count] );
- HsoiForgetResource( (Handle *)&gMyCursTask.myCursors[count] );
- }
-
- // or, if you didn't want to rely on HAS's ForgetResource function, just call this:
- // ReleaseResource( (Handle)gMyCursTask.myCursors[count] );
-
- // dispose our routine descriptor
-
- DisposeRoutineDescriptor( gMyVBLUPP );
- gMyVBLUPP = nil; // make sure the handle is nilled out so we "can't" use it accidentally
-
- // and set the cursor up nicely
-
- InitCursor();
-
- return;
- }